/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Server;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.security.NoSuchAlgorithmException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author TienNT
 */
public class ServThread extends Thread {

    private String mOdbcName;
    private DatabaseAdapter daAd;
    private ResultSet rs;
    private SimpleMD5 md = new SimpleMD5();
    private ServerControl.ControlThread mControlThread;
    private String mServerName;
    private String mClientName;
    private Socket mSocket;
    private BufferedReader mFromClient;
    private PrintWriter mToClient;
    public static int numOfServThread = 1;
    public static int groupId = 1;
    public static Map<String, ServThread> mSocketTable = new HashMap<>();
    public static HashMap<Integer, ArrayList<String>> mGroupChatTable = new HashMap<>();
    // Map co key la id cua group, value la 1 arraylist chua cac thanh vien tham gia group

    public ServThread(ServerControl.ControlThread controlThread, Socket socket, String OdbcName) throws SQLException, IOException {
        mOdbcName = OdbcName;
        daAd = new DatabaseAdapter(mOdbcName);
        mSocket = socket;
        mControlThread = controlThread;
        mServerName = "Server thread " + this.numOfServThread++;
        mControlThread.appendLog(mServerName + ":   " + "start...");
    }

    public void sendMessageToClient(String message) {
        mToClient.println(message);
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        try {
            mFromClient = new BufferedReader(new InputStreamReader(
                    mSocket.getInputStream()));
            mToClient = new PrintWriter(mSocket.getOutputStream(), true);

        } catch (IOException e) {
            // TODO Auto-generated catch block
            mControlThread.appendLog(mServerName + ":   " + "Error...");
            mControlThread.appendLog(e.getMessage());
        }
        try {

            while (true) {

                final String s = mFromClient.readLine();
                if (s == null) {
                    return;
                }
                String request = s.substring(0, 2);
                switch (request) {
                    case ServerUtils.STOP_SERVER:
                        processStopServer();
                        return;
                    case ServerUtils.ASK_FILE_TRANSFER_OK:
                        processAskFileTransferOk(s);
                        break;
                    case ServerUtils.ASK_FILE_TRANSFER:
                        processAskFileTransfer(s);
                        break;
                    case ServerUtils.ADD_IGNORE:
                        addIgnore(s);
                        break;
                    case ServerUtils.REQUEST_LOGIN:
                        processLoginRequest(s);
                        break;
                    case ServerUtils.REQUEST_LOGOUT:
                        processLogoutRequest();
                        return;
                    case ServerUtils.NEW_PRIVATE_MESSAGE:
                        processSendNewPrivateMessageRequest(s);
                        break;
                    case ServerUtils.REQUEST_REGISTER:
                        processRegistrationRequest(s);
                        break;
                    case ServerUtils.REQUEST_CHANGEPASSWORD:
                        processChangePasswordRequest(s);
                        break;
                    case ServerUtils.REQUEST_CHANGE_STATUS:
                        processChangeStatusRequest(s);
                        break;
                    case ServerUtils.REQUEST_GROUP_CHAT:
                        processSendGroupChatRequest(s);
                        break;
                    case ServerUtils.GROUP_CHAT_ACCEPT:
                        processGroupChatAccept(s);
                        break;
                    case ServerUtils.GROUP_CHAT_DECLINE:
                        processGroupChatDecline(s);
                        break;
                    case ServerUtils.BUZZ_REQUEST:
                        processSendBuzzRequest(s);
                        break;
                    case ServerUtils.NEW_GROUP_MESSAGE:
                        processSendNewGroupMessage(s);
                        break;
                    case ServerUtils.REQUEST_OUT_GROUP:
                        processKickOutGroup(s);
                        break;
                    case ServerUtils.FILE_TRANSFERING_TOSERVER:
                        processFileTransferingToServer(s);
                        break;
                    case ServerUtils.NOT_ACCEPT:

                        break;
                    default:
                        break;
                }
            }

        } catch (IOException ex) {
            mControlThread.appendLog(mServerName + ":   " + "Error...");
            mControlThread.appendLog(ex.getMessage());
        }
    }

    private void processStopServer(){
        try {
            mControlThread.appendLog("Server is stopping...");
            
            mControlThread.appendLog("Server stopped.");
            mFromClient.close();
            mToClient.close();
            mSocket.close();
        } catch (Exception ex) {
        }
    }
    
    public void sendIgnoreList() throws ClassNotFoundException, SQLException {
        mToClient.println(ServerUtils.IGNORE_LIST);
        ResultSet rs = daAd.executeQuery("select * from userignore where username='" + mClientName + "'");
        String ignoredList = "";
        while (rs.next()) {
            ignoredList += rs.getString(2);
            ignoredList += ":";
        }
        if (ignoredList.length() != 0) {
            ignoredList = ignoredList.substring(0, ignoredList.length() - 1);
        }

        System.out.println("ign" + ignoredList);
        mToClient.println(ignoredList);
    }

    public void kickRequested() {
        mToClient.println(ServerUtils.REQUEST_FORCE_LOGOUT);
        processLogoutRequest();
    }

    public void notifyContactListChanged(String code, String username, boolean isOnline, String status) {
        Set servThreadList = (Set) mSocketTable.keySet();
        for (Iterator i = servThreadList.iterator(); i.hasNext();) {
            String k = (String) i.next();
            mSocketTable.get(k).sendMessageToClient("" + ServerUtils.CONTACT_LIST_CHANGED + ":" + code + ":" + username + ":" + status + ":" + isOnline);;
        }
    }

    public void sendContactList() throws ClassNotFoundException, SQLException {
        int size = 0;
        String username;
        Boolean isOnline;
        String status;
        ResultSet r;
        r = daAd.executeQuery("select * from userinfo");
        while (true) {
            if (r.next()) {
                size++;
            } else {
                break;
            }
        }
        sendMessageToClient(ServerUtils.SEND_CONTACT_LIST);
        sendMessageToClient("" + size);
        r = daAd.executeQuery("select * from userinfo");
        while (r.next()) {
            username = r.getString(ServerUtils.DB_COLUMN_USERNAME);
            status = r.getString(ServerUtils.DB_COLUMN_STATUS);
            if (mSocketTable.containsKey(username)) {
                isOnline = true;
            } else {
                isOnline = false;
            }
            sendMessageToClient(username + ":" + status + ":" + isOnline);
        }
    }

    private void processRegistrationRequest(String s) {
        String[] strings = s.split(":");
        String username = strings[1];
        String password = strings[2];
        mControlThread.appendLog(username + "Request register");
        try {
            ResultSet rs = daAd.executeQuery("select * from userinfo where username='" + username + "'");
            if (!rs.next()) {
                String temp1 = md.MD5(password);
                daAd.executeUpdate("insert into userinfo values('" + username + "','" + temp1 + "','')");
                sendMessageToClient("" + ServerUtils.ACCEPT);
                notifyContactListChanged(ServerUtils.CODE_REGISTER, username, false, "");
            } else {
                sendMessageToClient("" + ServerUtils.NOT_ACCEPT);
            }
        } catch (ClassNotFoundException | SQLException | NoSuchAlgorithmException | UnsupportedEncodingException e) {
            mControlThread.appendLog(username + "Regiter exception");
            sendMessageToClient("" + ServerUtils.NOT_ACCEPT);
        } finally {
        }
    }

    private void processChangePasswordRequest(String s) {
        String[] strings = s.split(":");
        String username = strings[1];
        String password = strings[2];
        String newpassword = strings[3];

        mControlThread.appendLog(username + "Request change password");
        try {
            String temp1 = md.MD5(password);
            rs = daAd.executeQuery("select * from userinfo where username='" + username + "' and password='" + temp1 + "'");
            if (!rs.next()) {
                sendMessageToClient("" + ServerUtils.NOT_ACCEPT);
            } else {
                String temp2 = md.MD5(newpassword);
                daAd.executeUpdate("update userinfo set password='" + temp2 + "' where username='" + username + "'");
                sendMessageToClient("" + ServerUtils.ACCEPT);
            }

        } catch (ClassNotFoundException | SQLException | UnsupportedEncodingException | NoSuchAlgorithmException ex) {
        }
    }

    private void processChangeStatusRequest(String s) {
        System.out.println("status" + s);
        String[] strings = s.split(":");
        String u = strings[1];
        String st = strings[2];
        try {
            daAd.executeUpdate("update userinfo set status='" + st + "' where username='" + u + "'");
        } catch (ClassNotFoundException | SQLException ex) {
        }
        notifyContactListChanged(ServerUtils.CODE_STATUS_CHANGE, u, true, st);
    }

    private void processOfflineMessage() {
        String sql = "select * from offlinemessage where touser='" + mClientName + "'";
        String s;
        int id;
        try {
            ResultSet rs = daAd.executeQuery(sql);
            while (rs.next()) {
                id = rs.getInt("id");
                s = "" + ServerUtils.NEW_PRIVATE_MESSAGE + ":" + rs.getString("fromuser")
                        + ":" + rs.getString("touser")
                        + ":" + rs.getString("content") + "(offline)";
                sendMessageToClient(s);
            }
            sql = "delete from offlinemessage where touser='" + mClientName + "'";
            daAd.executeUpdate(sql);
        } catch (Exception e) {
        }
    }

    private void processLoginRequest(String s) {
        String[] strings = s.split(":");
        String temp1 = "", temp2 = "";
        mClientName = strings[1];
        mControlThread.appendLog(mClientName + " has connected.");
        String password = strings[2];
        try {
            temp1 = md.MD5(password);
            rs = daAd.executeQuery("select * from userinfo where username='" + mClientName + "'");
            if (rs.next()) {
                temp2 = rs.getString(ServerUtils.DB_COLUMN_PASSWORD);
            }
            if (temp1.equals(temp2) && temp1 != null) {
                if (mSocketTable.containsKey(mClientName)) {

                    mSocketTable.get(mClientName).kickRequested();

                }
                mSocketTable.put(mClientName, this);
                sendMessageToClient("" + ServerUtils.ACCEPT);
                mControlThread.appendLog(mClientName + " login successfully!");
                processOfflineMessage();
                // Transfer contact list
                sendContactList();
                notifyContactListChanged(ServerUtils.CODE_LOGIN, mClientName, true, "");
                sendIgnoreList();
            } else {
                sendMessageToClient("" + ServerUtils.NOT_ACCEPT);
            }
        } catch (NoSuchAlgorithmException | UnsupportedEncodingException | ClassNotFoundException | SQLException ex) {
            sendMessageToClient("" + ServerUtils.NOT_ACCEPT);
        }
    }

    private void processLogoutRequest() {
        try {
            mControlThread.appendLog(mClientName + " request to logout.");
            mSocketTable.remove(mClientName);
            mControlThread.appendLog(mClientName + " has loged out!");
            notifyContactListChanged(ServerUtils.CODE_LOGOUT, mClientName, false, "");
//            stop = true;
            mFromClient.close();
            mToClient.close();
            mSocket.close();
        } catch (Exception ex) {
        }
    }

    private void processSendNewPrivateMessageRequest(String s) {
        /**
         * Command structure: ClientUtils.NEW_PRIVATE_MESSAGE + ":" + source +
         * ":" + destination + ":" + message
         */
        String[] s2 = s.split(":");
        String desClient = s2[2];
        if (mSocketTable.containsKey(s2[2])) {
            mSocketTable.get(desClient).sendMessageToClient(s);
        } else {
            String sql = "insert into offlinemessage values('" + s2[1] + "','"
                    + s2[2] + "','" + s2[3] + "')";
            try {
                daAd.executeUpdate(sql);
            } catch (ClassNotFoundException | SQLException ex) {
                mControlThread.appendLog(mClientName + " error");
            }
        }
    }

    private void processSendGroupChatRequest(String s) {
        /**
         * Command structure: Client.ClientUtils.REQUEST_GROUP_CHAT + ":" +
         * selectedContact + ":" + invitation
         */
        ArrayList<String> groupChat = new ArrayList<>();
        groupChat.add(mClientName);

        // Tra group id ve cho sender de sender tao group conversasion
        sendMessageToClient(ServerUtils.GROUP_ID_COMMING + ":" + groupId);

        mGroupChatTable.put(groupId, groupChat);

        String[] strings = s.split(":");
        String selectedContact = strings[1];
        String message = "";
        if (strings.length == 3) {
            message = strings[2];
        }
        String command = ServerUtils.REQUEST_GROUP_CHAT + ":" + groupId + ":" + mClientName + ":" + message;

        String[] listInvitedContact = selectedContact.split("-");

        for (String contact : listInvitedContact) {
            mSocketTable.get(contact).sendMessageToClient(command);
        }
        groupId++;
    }

    private void processGroupChatAccept(String s) {
        int id = Integer.parseInt(s.substring(3));
        ArrayList<String> listJoinedContact = ServThread.mGroupChatTable.get(id);
        for (int i = 0; i < listJoinedContact.size(); i++) {
            mSocketTable.get(listJoinedContact.get(i)).sendMessageToClient(ServerUtils.CONTACT_JOINED_GROUP + ":" + mClientName + ":" + id);
        }
        listJoinedContact.add(mClientName);
    }

    private void processGroupChatDecline(String s) {
        int id = Integer.parseInt(s.substring(3));
        ArrayList<String> listJoinedContact = ServThread.mGroupChatTable.get(id);
        for (int i = 0; i < listJoinedContact.size(); i++) {
            mSocketTable.get(listJoinedContact.get(i)).sendMessageToClient(ServerUtils.CONTACT_NOT_JOINED_GROUP + ":" + mClientName + ":" + id);
        }
    }

    private void processSendBuzzRequest(String s) {
        String[] strings = s.split(":");
        if (mSocketTable.containsKey(strings[1])) {
            mSocketTable.get(strings[1]).sendMessageToClient(ServerUtils.BUZZ_INCOMING);
            mSocketTable.get(strings[1]).sendMessageToClient(mClientName);
        }

    }

    private void processSendNewGroupMessage(String s) {
        /**
         * Command structure: ClientUtils.NEW_GROUP_MESSAGE + ":" + mGroupId +
         * ":" + message;
         */
        String[] strings = s.split(":");
        int id = Integer.parseInt(strings[1]);
        String message = strings[2];
        String command = ServerUtils.NEW_GROUP_MESSAGE + ":" + id + ":" + mClientName + ":" + message;
        ArrayList<String> listJoinedContact = ServThread.mGroupChatTable.get(id);
        for (int i = 0; i < listJoinedContact.size(); i++) {
            mSocketTable.get(listJoinedContact.get(i)).sendMessageToClient(command);
        }
    }

    private void addIgnore(String s) {
        System.out.println("add ignore");
        String[] temp = s.split(":");
        if (temp.length == 1) {
            try {
                daAd.executeUpdate("delete from userignore where username='" + mClientName + "'");
            } catch (ClassNotFoundException ex) {
                Logger.getLogger(ServThread.class.getName()).log(Level.SEVERE, null, ex);
            } catch (SQLException ex) {
                Logger.getLogger(ServThread.class.getName()).log(Level.SEVERE, null, ex);
            }
            try {
                sendIgnoreList();
            } catch (ClassNotFoundException | SQLException ex) {
                Logger.getLogger(ServThread.class.getName()).log(Level.SEVERE, null, ex);
            }
            return;
        }

        StringBuilder sb = new StringBuilder();
        sb.append("insert into userignore values ");
        for (int i = 1; i < temp.length; i++) {
            sb.append("('" + mClientName + "','" + temp[i] + "'),");
        }
        sb.deleteCharAt(sb.length() - 1);
        String sql = new String(sb);
        try {
            daAd.executeUpdate("delete from userignore where username='" + mClientName + "'");
            daAd.executeUpdate(sql);
        } catch (ClassNotFoundException | SQLException ex) {
            Logger.getLogger(ServThread.class.getName()).log(Level.SEVERE, null, ex);
        }
        try {
            sendIgnoreList();
        } catch (ClassNotFoundException | SQLException ex) {
            Logger.getLogger(ServThread.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void processAskFileTransfer(String s) {
        String[] temps = s.split(";");
        String command = "" + ServerUtils.CONFIRM_FILE_TRANSFER_TOCLIENT + ";" + temps[1] + ";" + temps[2] + ";" + temps[3];
        if (mSocketTable.containsKey(temps[2])) {
            mSocketTable.get(temps[2]).sendMessageToClient(command);
        }
    }

    private void processAskFileTransferOk(String s) {
        String[] temps = s.split(";");
        String command = "" + ServerUtils.ASK_FILE_TRANSFER_OK + ";" + temps[1] + ";" + temps[2] + ";" + temps[3];
        if (mSocketTable.containsKey(temps[1])) {
            mSocketTable.get(temps[1]).sendMessageToClient(command);
        }
    }

    private void processKickOutGroup(String s) {
        String[] strings = s.split(":");
        int id = Integer.parseInt(strings[1]);
        sendMessageToClient(ServerUtils.FROM_SERVER_KICKOUT_GROUP + ":" + id);
        ArrayList<String> listJoinedContact = ServThread.mGroupChatTable.get(id);
        listJoinedContact.remove(mClientName);
        for (int i = 0; i < listJoinedContact.size(); i++) {
            mSocketTable.get(listJoinedContact.get(i)).sendMessageToClient(ServerUtils.CONTACT_LEAVE_GROUP + ":" + mClientName + ":" + id);
        }
    }

    private void processFileTransferingToServer(String s) {
        String[] temps = s.split(";");
        System.out.println(s);
        try {
            byte[] temparray = new byte[Integer.parseInt(temps[4])];
            
            OutputStream os = mSocket.getOutputStream();
            BufferedInputStream is = new BufferedInputStream(mSocket.getInputStream());
            int read = 0;
            read = is.read(temparray, 0, temparray.length);
            mSocketTable.get(temps[2]).sendMessageToClient("" + ServerUtils.FILE_TRANSFERING_TOCLIENT + ";" + temps[4]);
            mSocketTable.get(temps[2]).mSocket.getOutputStream().write(temparray);
            mSocketTable.get(temps[2]).mSocket.getOutputStream().flush();


          //  fos.write(temparray);
           // fos.flush();
        } catch (IOException e) {
            System.out.println("error");
        }

    }
}
